from pathlib import Path
from queue import Queue
import threading, os
from .view import *
from fplotkit import NetPlot, ReadOnlyStatistics
from tkinter import filedialog
from tkinter import messagebox as MB

ITEM_ALL = "<所有>"
ITEM_ALL_FAST = "<所有快充站>"
ITEM_ALL_SLOW = "<所有慢充站>"
ITEM_ALL_G = "<所有普通发电机>"
ITEM_ALL_V2G = "<所有充电站V2G>"
ITEM_LOADING = "加载中……"

class _PlotBoxGUI(Tk):
    def __init__(self):
        super().__init__()
        self._win()

        self.panel_trips = self._panel_trips(self)
        self.btn_trips = self._btn_trips(self.panel_trips)

        self.panel_cspk = self._panel_cspk(self)
        self.cb_cspk1 = self._cb_cspk1(self.panel_cspk) 
        self.cb_cspk2 = self._cb_cspk2(self.panel_cspk) 
        self.cb_cspk3 = self._cb_cspk3(self.panel_cspk) 
        self.cb_cspk4 = self._cb_cspk4(self.panel_cspk) 
        self.cb_cspk5 = self._cb_cspk5(self.panel_cspk) 
        self.btn_cspk = self._btn_cspk(self.panel_cspk)
        self.cs_entry = self._cs_entry(self.panel_cspk)
        self.btn_cspk_accum = self._btn_cspk_accum(self.panel_cspk)

        self.panel_ev = self._panel_ev(self)
        self.btn_ev = self._btn_ev(self.panel_ev) 
        self.cb_ev1 = self._cb_ev1(self.panel_ev)
        self.cb_ev2 = self._cb_ev2(self.panel_ev) 
        self.cb_ev3 = self._cb_ev3(self.panel_ev) 
        self.cb_ev4 = self._cb_ev4(self.panel_ev) 
        self.cb_ev5 = self._cb_ev5(self.panel_ev) 
        self.ev_entry = self._ev_entry(self.panel_ev) 
        self.btn_route = self._btn_route(self.panel_ev) 

        self.panel_gen = self._panel_gen(self)
        self.btn_gen = self._btn_gen(self.panel_gen) 
        self.cb_gen1 = self._cb_gen1(self.panel_gen)
        self.cb_gen2 = self._cb_gen2(self.panel_gen) 
        self.cb_gen3 = self._cb_gen3(self.panel_gen) 
        self.gen_entry = self._gen_entry(self.panel_gen) 
        self.btn_Gtotal = self._btn_Gtotal(self.panel_gen)

        self.panel_bus = self._panel_bus(self)
        self.btn_bus = self._btn_bus(self.panel_bus) 
        self.cb_bus1 = self._cb_bus1(self.panel_bus) 
        self.cb_bus2 = self._cb_bus2(self.panel_bus) 
        self.cb_bus3 = self._cb_bus3(self.panel_bus)
        self.cb_bus4 = self._cb_bus4(self.panel_bus) 
        self.cb_bus5 = self._cb_bus5(self.panel_bus)
        self.bus_entry = self._bus_entry(self.panel_bus) 
        
        self._sbar=Label(self,text=ITEM_LOADING)
        self._sbar.pack(side='bottom',anchor='w',padx=3,pady=3)
    def _win(self):
        self.title("Plot Box")
        # 设置窗口大小、居中
        width = 640
        height = 560
        screenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.geometry(geometry)
        
        self.resizable(width=False, height=False)
    def _btn_Gtotal(self,parent):
        btn = Button(parent, text="绘制总曲线", takefocus=False,)
        btn.place(x=405, y=0, width=100, height=30)
        return btn
    def _panel_trips(self,parent):
        frame = LabelFrame(parent,text="行程 Trips",)
        frame.place(x=40, y=20, width=560, height=60)
        return frame
    def _btn_trips(self,parent):
        btn = Button(parent, text="绘制直方图", takefocus=False,)
        btn.place(x=10, y=0, width=100, height=30)
        return btn
    def _panel_cspk(self,parent):
        frame = LabelFrame(parent,text="充电站 Charging Stations",)
        frame.place(x=40, y=90, width=560, height=100)
        return frame
    def _cb_cspk1(self,parent):
        self._cs_wcnt = BooleanVar(parent,True,"cs_wcnt")
        cb = Checkbutton(parent,text="车辆数",variable=self._cs_wcnt)
        cb.place(x=10, y=0, width=75, height=30)
        return cb
    def _cb_cspk2(self,parent):
        self._cs_pc = BooleanVar(parent,True,"cs_pc")
        cb = Checkbutton(parent,text="充电功率",variable=self._cs_pc)
        cb.place(x=90, y=0, width=90, height=30)
        return cb
    def _cb_cspk3(self,parent):
        self._cs_pd = BooleanVar(parent,True,"cs_pd")
        cb = Checkbutton(parent,text="V2G放电功率",variable=self._cs_pd)
        cb.place(x=180, y=0, width=120, height=30)
        return cb
    def _cb_cspk4(self,parent):
        self._cs_ppure = BooleanVar(parent,True,"cs_ppure")
        cb = Checkbutton(parent,text="净充电功率",variable=self._cs_ppure)
        cb.place(x=300, y=0, width=110, height=30)
        return cb
    def _cb_cspk5(self,parent):
        self._cs_pv2g = BooleanVar(parent,True,"cs_pv2g")
        cb = Checkbutton(parent,text="V2G最大放电功率",variable=self._cs_pv2g)
        cb.place(x=400, y=0, width=150, height=30)
        return cb
    def _btn_cspk(self,parent):
        btn = Button(parent, text="绘制曲线图", takefocus=False,)
        btn.place(x=10, y=40, width=100, height=30)
        return btn
    def _btn_cspk_accum(self,parent):
        btn = Button(parent, text="绘制堆积图", takefocus=False,)
        btn.place(x=450, y=40, width=100, height=30)
        return btn
    def _cs_entry(self,parent):
        cb = Combobox(parent,)
        cb['values'] = []
        cb.place(x=120, y=40, width=315, height=30)
        return cb
    def _panel_ev(self,parent):
        frame = LabelFrame(parent,text="电动车 EVs",)
        frame.place(x=40, y=200, width=560, height=100)
        return frame
    def _cb_ev1(self,parent):
        self._ev_soc = BooleanVar(parent,True,"ev_soc")
        cb = Checkbutton(parent,text="SoC",variable=self._ev_soc)
        cb.place(x=10, y=0, width=60, height=30)
        return cb
    def _btn_ev(self,parent):
        btn = Button(parent, text="绘制曲线图", takefocus=False,)
        btn.place(x=10, y=40, width=100, height=30)
        return btn
    def _cb_ev2(self,parent):
        self._ev_sta = BooleanVar(parent,True,"ev_sta")
        cb = Checkbutton(parent,text="状态",variable=self._ev_sta)
        cb.place(x=75, y=0, width=70, height=30)
        return cb
    def _cb_ev3(self,parent):
        self._ev_cost = BooleanVar(parent,True,"ev_cost")
        cb = Checkbutton(parent,text="充电花费",variable=self._ev_cost)
        cb.place(x=140, y=0, width=105, height=30)
        return cb
    def _cb_ev4(self,parent):
        self._ev_earn = BooleanVar(parent,True,"ev_earn")
        cb = Checkbutton(parent,text="放电收益",variable=self._ev_earn)
        cb.place(x=235, y=0, width=105, height=30)
        return cb
    def _cb_ev5(self,parent):
        self._ev_cpure = BooleanVar(parent,True,"ev_cpure")
        cb = Checkbutton(parent,text="净花费",variable=self._ev_cpure)
        cb.place(x=325, y=0, width=75, height=30)
        return cb
    def _ev_entry(self,parent):
        ipt = Entry(parent, )
        ipt.place(x=120, y=40, width=430, height=30)
        return ipt
    def _btn_route(self,parent):
        btn = Button(parent, text="绘制路径图", takefocus=False,)
        btn.place(x=405, y=0, width=100, height=30)
        return btn
    def _panel_gen(self,parent):
        frame = LabelFrame(parent,text="发电机 Generators",)
        frame.place(x=40, y=310, width=560, height=100)
        return frame
    def _cb_gen1(self,parent):
        self._g_p = BooleanVar(parent,True,"g_p")
        cb = Checkbutton(parent,text="有功功率",variable=self._g_p)
        cb.place(x=10, y=0, width=90, height=30)
        return cb
    def _btn_gen(self,parent):
        btn = Button(parent, text="绘制曲线图", takefocus=False,)
        btn.place(x=10, y=40, width=100, height=30)
        return btn
    def _cb_gen2(self,parent):
        self._g_q = BooleanVar(parent,True,"g_q")
        cb = Checkbutton(parent,text="无功功率",variable=self._g_q)
        cb.place(x=105, y=0, width=90, height=30)
        return cb
    def _cb_gen3(self,parent):
        self._g_cost = BooleanVar(parent,True,"g_cost")
        cb = Checkbutton(parent,text="平均发电成本",variable=self._g_cost)
        cb.place(x=200, y=0, width=130, height=30)
        return cb
    def _gen_entry(self,parent):
        cb = Combobox(parent,)
        cb['values'] = []
        cb.place(x=120, y=40, width=430, height=30)
        return cb
    def _panel_bus(self,parent):
        frame = LabelFrame(parent,text="母线 Buses",)
        frame.place(x=40, y=420, width=560, height=100)
        return frame
    def _cb_bus1(self,parent):
        self._bus_pd = BooleanVar(parent,True,"bus_pd")
        cb = Checkbutton(parent,text="有功负荷",variable=self._bus_pd)
        cb.place(x=10, y=0, width=90, height=30)
        return cb
    def _btn_bus(self,parent):
        btn = Button(parent, text="绘制曲线图", takefocus=False,)
        btn.place(x=10, y=40, width=100, height=30)
        return btn
    def _cb_bus2(self,parent):
        self._bus_qd = BooleanVar(parent,True,"bus_qd")
        cb = Checkbutton(parent,text="无功负荷",variable=self._bus_qd)
        cb.place(x=105, y=0, width=90, height=30)
        return cb
    def _cb_bus3(self,parent):
        self._bus_cmg = BooleanVar(parent,True,"bus_cmg")
        cb = Checkbutton(parent,text="影子价格",variable=self._bus_cmg)
        cb.place(x=390, y=0, width=90, height=30)
        return cb
    def _bus_entry(self,parent):
        cb = Combobox(parent,)
        cb['values'] = []
        cb.place(x=120, y=40, width=430, height=30)
        return cb
    def _cb_bus4(self,parent):
        self._bus_pg = BooleanVar(parent,True,"bus_pg")
        cb = Checkbutton(parent,text="有功发电",variable=self._bus_pg)
        cb.place(x=200, y=0, width=90, height=30)
        return cb
    def _cb_bus5(self,parent):
        self._bus_qg = BooleanVar(parent,True,"bus_qg")
        cb = Checkbutton(parent,text="无功发电",variable=self._bus_qg)
        cb.place(x=295, y=0, width=90, height=30)
        return cb

class PlotBox(_PlotBoxGUI):
    _sta:ReadOnlyStatistics
    _npl:NetPlot

    def __init__(self):
        super().__init__()
        self.btn_trips.configure(command=self.plotTripsHist)
        self.btn_cspk.configure(command=self.plotCSCurve)
        self.btn_cspk_accum.configure(command=self.plotCSAccum)
        self.btn_ev.configure(command=self.plotEVCurve)
        self.btn_route.configure(command=self.plotEVRoute)
        self.btn_gen.configure(command=self.plotGCurve)
        self.btn_Gtotal.configure(command=self.plotGTotal)
        self.btn_bus.configure(command=self.plotBusCurve)

        self._ava ={
            "trips": [False, self.panel_trips],
            "cspk": [False, self.panel_cspk],
            "ev": [False, self.panel_ev],
            "gen": [False, self.panel_gen],
            "bus": [False, self.panel_bus],
        }
        self._Q = Queue()
        self.disable_all()
        threading.Thread(target=self.reload,daemon=True,args=("results",)).start()

        self.after(100,self._upd)

    def disable_all(self):
        for ok, panel in self._ava.values():
            for child in panel.children.values():
                child['state']=DISABLED

    def enable_all(self):
        for ok, panel in self._ava.values():
            if not ok: continue
            for child in panel.children.values():
                child['state']=NORMAL
    
    def set_status(self,text:str):
        self._sbar.configure(text=text)

    def _upd(self):
        while not self._Q.empty():
            op,*par=self._Q.get()
            if op=='L':
                self._sta,self._npl,trips_file=par
                assert isinstance(self._sta,ReadOnlyStatistics)
                self._ava["trips"][0] = trips_file is not None
                if self._sta.has_CS():
                    self._ava["cspk"][0] = True
                    self.cs_entry['values'] = [ITEM_ALL,ITEM_ALL_FAST,ITEM_ALL_SLOW] + self._sta.CS_head
                    self.cs_entry.set(ITEM_ALL)
                self._ava["ev"][0] = self._sta.has_EV()
                if self._sta.has_GEN():
                    self._ava["gen"][0] = True
                    self.gen_entry['values'] = [ITEM_ALL,ITEM_ALL_G,ITEM_ALL_V2G] + self._sta.gen_head
                    self.gen_entry.set(ITEM_ALL)
                if self._sta.has_BUS():
                    self._ava["bus"][0] = True
                    self.bus_entry['values'] = [ITEM_ALL] + self._sta.bus_head
                    self.bus_entry.set(ITEM_ALL)
                self.set_status("就绪")
                self.enable_all()
            elif op=='I':
                self.set_status(par[0])
            elif op=='E':
                self.set_status(par[0])
                self.enable_all()
            elif op=='LE':
                self.set_status(par[0])
                break
            elif op=='D':
                self.set_status("就绪")
                self.enable_all()
            elif op=='Q':
                self.destroy()
            else:
                self.set_status("出现内部错误!")
                break
        self.after(100,self._upd)
    
    def reload(self,res_path):
        try:
            first = True
            while True:
                res_path = Path(res_path)
                if res_path.exists():
                    if (res_path / "time_line.pkl.gz").exists():
                        break
                    else: MB.showerror("加载失败","未找到时间轴文件")
                else: 
                    if not first: MB.showerror("加载失败","文件夹不存在")
                first = False
                res_path = filedialog.askdirectory(title="请选择仿真结果文件夹",initialdir=os.getcwd())
                if res_path=="":
                    self._Q.put(('Q',None))
                    return
            sta = ReadOnlyStatistics(str(res_path))
            trips_file = None
            for file in os.listdir(res_path):
                if file.lower().endswith("veh.xml"): 
                    trips_file = file
                    break
            if trips_file is None: 
                trips_file = filedialog.askopenfilename(title="请选择行程文件",filetypes=[("行程XML文件",".veh.xml")],initialdir=os.getcwd())
                if trips_file=="": trips_file = None
            nplt = NetPlot(sta,trips_file,save_to=str(res_path/"figures"))
            self._Q.put(('L',sta,nplt,trips_file))
        except Exception as e:
            self._Q.put(('LE',e))

    def plotTripsHist(self):
        self.disable_all()
        self.set_status("绘制行程直方图……")
        def work():
            try:
                self._npl.trip_hist()
            except Exception as e:
                self._Q.put(('E',f'绘制行程直方图时遇到错误: {e}'))
                return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()

    def plotCSAccum(self):
        self.disable_all()
        self.set_status("绘制负荷堆积图……")
        def work():
            try:
                self._npl.cs_load_accum()
            except Exception as e:
                self._Q.put(('E',f'绘制负荷堆积图时遇到错误: {e}'))
                return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()
    
    def plotCSCurve(self):
        self.disable_all()
        self.set_status("绘制CS曲线图……")
        def work():
            t=self.cs_entry.get()
            if t.strip()=="" or t==ITEM_ALL:
                cs = self._sta.CS_head
            elif t==ITEM_ALL_FAST:
                cs = [x for x in self._sta.CS_head if x.startswith("CS")]
            elif t==ITEM_ALL_SLOW:
                cs = [x for x in self._sta.CS_head if not x.startswith("CS")]
            else:
                cs = [x.strip() for x in t.split(',')]
            for i,c in enumerate(cs,start=1):
                try:
                    self._Q.put(('I',f'({i}/{len(cs)})绘制负荷曲线图……'))
                    loadd=self._cs_pd.get()
                    loadt=self._cs_ppure.get()
                    loaddm=self._cs_pv2g.get()
                    if c.startswith("CS"):
                        loadd = loadt = loaddm = False
                    self._npl.cs_curve(c,
                        wcnt=self._cs_wcnt.get(),
                        loadc=self._cs_pc.get(),
                        loadd=loadd,
                        loadt=loadt,
                        loaddm=loaddm
                    )
                except Exception as e:
                    self._Q.put(('E',f'绘制CS曲线图时遇到错误: {e}'))
                    return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()
    
    def plotEVCurve(self):
        self.disable_all()
        self.set_status("绘制EV参数曲线……")
        def work():
            t=self.ev_entry.get()
            evs=None if t.strip()=="" else [x.strip() for x in t.split(',')]
            if evs is None:
                self._Q.put(('E','车辆ID不能为空'))
                return
            for ev in evs:
                try:
                    attrs = []
                    if self._ev_cost.get(): attrs.append('cost')
                    if self._ev_earn.get(): attrs.append('earn')
                    if self._ev_cpure.get(): attrs.append('cpure')
                    if self._ev_soc.get(): attrs.append('soc')
                    if self._ev_sta.get(): attrs.append('status')
                    self._npl.ev_attrib(ev,attrs)
                except Exception as e:
                    self._Q.put(('E',f'绘制车辆{ev}的参数曲线时出现错误: {e}'))
                    return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()

    def plotEVRoute(self):
        self.disable_all()
        self.set_status("绘制EV路径图……")
        def work():
            t=self.ev_entry.get()
            evs=None if t.strip()=="" else [x.strip() for x in t.split(',')]
            if evs is None:
                self._Q.put(('E','车辆ID不能为空'))
                return
            for ev in evs:
                try:
                    self._npl.ev_route(ev)
                except Exception as e:
                    self._Q.put(('E',f'绘制车辆{ev}路径时出现错误: {e}'))
                    return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()
    
    def plotGTotal(self):
        self.disable_all()
        self.set_status("绘制总发电曲线……")
        def work():
            try:
                self._Q.put(('I',f'绘制总发电曲线……'))
                self._npl.gen_total()
            except Exception as e:
                self._Q.put(('E',f'绘制发电成本曲线时遇到错误: {e}'))
                return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()

    def plotGCurve(self):
        self.disable_all()
        self.set_status("绘制发电机曲线图……")
        def work():
            t=self.gen_entry.get()
            if t.strip()=="" or t==ITEM_ALL:
                gen=self._sta.gen_head
            elif t==ITEM_ALL_G:
                gen = [x for x in self._sta.gen_head if not x.startswith("V2G")]
            elif t==ITEM_ALL_V2G:
                gen = [x for x in self._sta.gen_head if x.startswith("V2G")]
            else: gen=[x.strip() for x in t.split(',')]
            for i,g in enumerate(gen,start=1):
                try:
                    self._Q.put(('I',f'({i}/{len(gen)})绘制发电机曲线图……'))
                    self._npl.gen_curve(g)
                except Exception as e:
                    self._Q.put(('E',f'绘制发电机曲线图时遇到错误: {e}'))
                    return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()

    def plotBusCurve(self):
        self.disable_all()
        self.set_status("绘制发电机曲线图……")
        def work():
            t=self.bus_entry.get()
            if t.strip()=="" or t==ITEM_ALL:
                bus=self._sta.bus_head
            else: bus=[x.strip() for x in t.split(',')]
            for i,g in enumerate(bus,start=1):
                try:
                    self._Q.put(('I',f'({i}/{len(bus)})绘制母线曲线图……'))
                    self._npl.bus_curve(g)
                except Exception as e:
                    self._Q.put(('E',f'绘制母线曲线图时遇到错误: {e}'))
                    return
            self._Q.put(('D',None))
        threading.Thread(target=work,daemon=True).start()
    
if __name__ == "__main__":
    win = PlotBox()
    win.mainloop()